package com.hex.ds.hdrs.period.job.po;

import cn.hutool.core.util.StrUtil;
import com.hex.bigdata.hbda.utils.StringUtils;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.utils.HdrsDateUtil;
import com.hex.ds.hdrs.common.utils.ListUtil;
import com.hex.ds.hdrs.period.task.po.PeriodTask;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.ListUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Package: com.hex.ds.hdrs.period.job.po
 * @ClassName JobTask
 * @Description: 作业任务对象
 * @Author: jd.chen
 * @Date 2021/7/9 10:17
 * @Version v1.0
 **/
@Slf4j
@Data
public class JobTask {

    private PeriodJob periodJob;

    private List<PeriodTask> periodTasks;

    public JobTask(PeriodJob job, List<PeriodTask> taskList) {
        this.periodJob = job;
        this.periodTasks = taskList;
    }

    /**
     * 获取作业编码
     */
    public String getJobCode() {
        return periodJob != null ? periodJob.getJobCode() : "";
    }

    public String getJobStatus() {
        return periodJob != null ? periodJob.getJobStatus() : "";
    }

    /**
     * 获取作业业务日期
     */
    public String getBizDate() {
        return periodJob != null ? periodJob.getBizDate() : "";
    }

    /**
     * 获取作业更新日期
     */
    public String getUpTs() {
        return periodJob != null ? periodJob.getUpTs() : "";
    }

    /**
     * 获取应用编码
     *
     * @return
     */
    public String getAppCode() {
        return periodJob != null ? periodJob.getAppCode() : "";
    }

    /**
     * 通过taskCode获取任务
     *
     * @param taskCode
     * @return
     */
    public PeriodTask getTask(String taskCode) {
        return periodTasks.stream().filter(task -> taskCode.equals(task.getTaskCode())).findAny().orElse(null);
    }

    /**
     * 获取任务的上游任务
     *
     * @param task
     * @return
     */
    public PeriodTask getUpTask(PeriodTask task) {
        List<String> taskCodeList = taskSortInJob();
        int taskIndex = taskCodeList.indexOf(task.getTaskCode());

        if (taskIndex != 0) {
            return this.getTask(taskCodeList.get(taskIndex - 1));
        }
        return null;
    }

    /**
     * @Method: isFirstTask
     * @Param: [task]
     * @Return: boolean
     * @Description：判断是否为作业中第一个任务
     * @Author： jd.chen
     * @Date： 2021/5/27 18:14
     * @Version： V1.0
     */
    public boolean isFirstTask(PeriodTask task) {
        List<String> taskCodeList = taskSortInJob();
        return task.getTaskCode().equals(taskCodeList.get(0));
    }

    /**
     * @Method: updateJobStatus
     * @Param: [upTs, status]
     * @Return: void
     * @Description： 更新作业状态
     * @Author： jd.chen
     * @Date： 2021/7/9 10:27
     * @Version： V1.0
     */
    public void updateJobStatus(String upTs, String status) {
        periodJob.setUpTs(upTs);
        periodJob.setJobStatus(status);
    }

    /**
     * @Method: upJobStatusToDo
     * @Param: [upTs]
     * @Return: void
     * @Description： 更新作业状态--待处理
     * @Author： jd.chen
     * @Date： 2021/7/8 20:35
     * @Version： V1.0
     */
    public void upJobStatusToDo(String upTs) {
        periodJob.setUpTs(upTs);
        periodJob.setJobStatus(Const.JOB_TO_DO);
        periodJob.setJobStTs("");
        periodJob.setJobEndTs("");
        periodJob.setErrorInfo("");
    }

    /**
     * @Method: upJobStatusToSucc
     * @Param: [upTs]
     * @Return: void
     * @Description： 更新作业状态--处理完成
     * @Author： jd.chen
     * @Date： 2021/7/8 20:35
     * @Version： V1.0
     */
    public void upJobStatusToSucc(String upTs) {
        periodJob.setUpTs(upTs);
        periodJob.setJobStatus(Const.JOB_DO_SUCCESS);
        periodJob.setJobStTs(StringUtils.defaultString(periodJob.getJobStTs(), upTs));
        periodJob.setJobEndTs(upTs);
    }

    /**
     * 更新任务状态
     *
     * @param taskCode
     * @param upTs
     * @param status
     */
    public void updateTaskStatus(String taskCode, String upTs, String status) {
        PeriodTask periodTask = this.getTask(taskCode);
        periodTask.setUpTs(upTs);
        periodTask.setTaskStatus(status);
    }

    /**
     * @Method: upTaskStatusToDo
     * @Param: [taskCode, upTs]
     * @Return: void
     * @Description： 更新任务状态--待处理
     * @Author： jd.chen
     * @Date： 2021/7/8 21:10
     * @Version： V1.0
     */
    public void upTaskStatusToDo(String taskCode, String upTs) {
        PeriodTask periodTask = this.getTask(taskCode);
        periodTask.setUpTs(upTs);
        periodTask.setTaskStatus(Const.TASK_TODO_RUN);
        periodTask.setExecStTs("");
        periodTask.setExecEndTs("");
        periodTask.setTaskExecLog("");
        periodTask.setTrackJson("");
        periodTask.setSysResetCount(0);
    }

    /**
     * @Method: upTaskStatusToHandSucc
     * @Param: [taskCode, upTs]
     * @Return: void
     * @Description： 更新所有任务状态--置虚
     * @Author： jd.chen
     * @Date： 2021/7/8 20:27
     * @Version： V1.0
     */
    public void upTaskStatusToHandSucc(String taskCode, String upTs) {
        PeriodTask periodTask = this.getTask(taskCode);
        periodTask.setUpTs(upTs);
        periodTask.setTaskStatus(Const.TASK_HAND_SUCCESS);
        periodTask.setExecStTs(StringUtils.defaultString(periodTask.getExecStTs(), upTs));
        periodTask.setExecEndTs(upTs);
    }

    /**
     * @Method: upTaskStatusToStop <br>
     * @Param: [taskCode, upTs] <br>
     * @Return: void <br>
     * @Description：更新所有任务状态--暂停<br>
     * @Author： gc.zuo<br>
     * @Date： 2022/1/26 14:31 <br>
     * @Version： V1.0<br>
     */
    public void upTaskStatusToStop(String taskCode, String upTs) {
        PeriodTask periodTask = this.getTask(taskCode);
        periodTask.setUpTs(upTs);
        periodTask.setTaskStatus(Const.TASK_STOP);
    }

    /**
     * @Method: isFinish
     * @Param: []
     * @Return: boolean
     * @Description： 判断作业是否结束
     * @Author： jd.chen
     * @Date： 2021/7/9 10:06
     * @Version： V1.0
     */
    public boolean isFinish() {
        return Const.JOB_DO_SUCCESS.equals(this.periodJob.getJobStatus());
    }

    /**
     * @Method: isAllTaskSucc
     * @Param: []
     * @Return: boolean
     * @Description： 判断所有任务是否都处理完成或者置虚
     * @Author： jd.chen
     * @Date： 2021/7/9 10:12
     * @Version： V1.0
     */
    public boolean isAllTaskSucc() {
        for (PeriodTask task : periodTasks) {
            String taskStatus = task.getTaskStatus();
            if (Const.TASK_SUCCESS.equals(taskStatus) || Const.TASK_HAND_SUCCESS.equals(taskStatus)) {
                continue;
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * @Method: isLstJob
     * @Param: []
     * @Return: boolean
     * @Description： 判断该作业实例是否最新业务日期的作业
     * @Author： jd.chen
     * @Date： 2021/7/9 10:06
     * @Version： V1.0
     */
    public boolean isLstJob() {
        return periodJob.getBizDate().equals(HdrsDateUtil.getCurrentDate());
    }

    /**
     * 获取有效的任务，设置当前任务
     *
     * @return
     */
    public synchronized PeriodTask getCurTask() {
        try {
            log.debug("【JobTask.getCurTask】获取作业有效的任务,作业编号:{},业务日期【{}】", this.getJobCode(), this.getBizDate());
            List<String> sortTaskList = taskSortInJob();

            if (ListUtil.isEmpty(sortTaskList)) {
                log.warn("【JobTask.getCurTask】获取作业有效的任务,任务定义集合为空,作业编号:{},业务日期【{}】", this.getJobCode(), this.getBizDate());
                return null;
            }

            for (String taskCode : sortTaskList) {
                PeriodTask task = this.getTask(taskCode);
                if (null == task) {
                    log.error("【JobTask.getCurTask】遍历获取作业有效的任务，任务定义为空,作业编号:{},业务日期:{},任务编码:{}", this.getJobCode(), this.getBizDate(), taskCode);
                    continue;
                }
                String taskStatus = task.getTaskStatus();
                if (Const.TASK_FAIL.equals(taskStatus)) {
                    log.warn("【JobTask.getCurTask】遍历获取作业有效的任务，任务:{}执行状态为失败,作业编号:{},业务日期:{}", taskCode, this.getJobCode(), this.getBizDate());
                    return null;
                } else if (Const.TASK_TODO_RUN.equals(taskStatus) || Const.TASK_RUNNING.equals(taskStatus) || Const.TASK_STOP.equals(taskStatus)) {
                    log.debug("【JobTask.getCurTask】遍历获取作业有效的任务，任务:{}的执行状态为:{},作业编号:{},业务日期:{}", taskCode, taskStatus, this.getJobCode(), this.getBizDate());
                    return task;
                } else {
                    log.warn("【JobTask.getCurTask】遍历获取作业有效的任务，任务:{}的执行状态为:{},作业编号:{},业务日期:{}", taskCode, taskStatus, this.getJobCode(), this.getBizDate());
                }
            }
            log.debug("获取有效的任务，设置当前任务【JobTask.getCurTask】结束《===作业编号:{},业务日期【{}】", this.getJobCode(), this.getBizDate());
            return null;
        } catch (Exception e) {
            log.error("【JobTask.getCurTask】获取作业有效的任务异常:{}", e.getMessage(), e);
            return null;
        }
    }

    /*
     * @Method: sortTaskCode <br>
     * @Param: [periodTasks] <br>
     * @Return: java.util.List<java.lang.String> <br>
     * @Description：本作业下任务进行排序<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/21 13:39 <br>
     * @Version： V2.0.2<br>
     */
    private List<String> taskSortInJob() {
        List<String> periodTaskCodeList = periodTasks.stream().map(PeriodTask::getTaskCode).collect(Collectors.toList());
        // 将ArrayList转换为Map,key为id,value为pid和该对象本身组成的List
        Map<String, List<PeriodTask>> periodTaskMap = buildPeriodTaskMap(periodTaskCodeList);
        // 根据pid的依赖进行拓扑排序
        Stack<PeriodTask> stack = stacekSort(periodTaskMap, periodTaskCodeList);
        // 输出排序后的结果
        List<String> sortList = new ArrayList<>();
        while (!stack.isEmpty()) {
            sortList.add(0, stack.pop().getTaskCode());
        }
        return sortList;
    }

    private Map<String, List<PeriodTask>> buildPeriodTaskMap(List<String> periodTaskCodeList) {
        Map<String, List<PeriodTask>> objectMap = new HashMap<>();
        for (PeriodTask periodTask : periodTasks) {
            List<String> dependTaskCodeList = ListUtils.intersection(Arrays.asList(StrUtil.split(periodTask.getDependTask(), ",")), periodTaskCodeList);
            List<PeriodTask> tempList = new ArrayList<>();
            tempList.add(periodTask);
            for (String dependTaskCode : dependTaskCodeList) {
                tempList.addAll(objectMap.getOrDefault(dependTaskCode, new ArrayList<>()));
            }
            objectMap.put(periodTask.getTaskCode(), tempList);
        }
        return objectMap;
    }

    private Stack<PeriodTask> stacekSort(Map<String, List<PeriodTask>> periodTaskMap, List<String> periodTaskCodeList) {
        Stack<PeriodTask> stack = new Stack<>();
        Map<String, Boolean> visited = new HashMap<>();
        for (String taskCode : periodTaskMap.keySet()) {
            if (!visited.containsKey(taskCode)) {
                dfs(taskCode, periodTaskMap, visited, stack, periodTaskCodeList);
            }
        }
        return stack;
    }

    private void dfs(String id, Map<String, List<PeriodTask>> objectMap, Map<String,
            Boolean> visited, Stack<PeriodTask> stack, List<String> periodTaskCodeList) {
        if (visited.containsKey(id)) {
            return;
        } else {
            visited.put(id, true);
            List<String> dependTaskCodeList = org.apache.commons.collections4.ListUtils.intersection(Arrays.asList(StrUtil.split(objectMap.get(id).get(0).getDependTask(), ",")), periodTaskCodeList);
            for (String childId : dependTaskCodeList) {
                dfs(childId, objectMap, visited, stack, periodTaskCodeList);
            }
            stack.push(objectMap.get(id).get(0));
        }
    }

    /**
     * 获取下一个任务
     *
     * @param curTask
     * @return
     */
    public String getNextTaskCode(String curTask) {
        List<String> sortTaskList = taskSortInJob();
        int curIndex = sortTaskList.indexOf(curTask);
        return curIndex == sortTaskList.size() - 1 ? "" : sortTaskList.get(curIndex + 1);
    }

    @Override
    public int hashCode() {
        int result = getJobCode() != null ? getJobCode().hashCode() : 0;
        result = result + getBizDate().hashCode();
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        JobTask jobTask = (JobTask) o;
        return StrUtil.equalsAnyIgnoreCase(this.getJobCode(), jobTask.getJobCode())
                && StrUtil.equalsAnyIgnoreCase(this.getBizDate(), jobTask.getBizDate());
    }
}
